package ooo.reindeer.cedf.components.proxy;

import ooo.reindeer.cedf.Cedf;
import ooo.reindeer.cedf.Event;
import ooo.reindeer.cedf.components.IProxyFactory;
import ooo.reindeer.cedf.components.handler.IEventHandler;
import ooo.reindeer.cedf.components.queue.IEventQueue;
import ooo.reindeer.commons.Property;
import ooo.reindeer.commons.utils.SerialNumberUtil;
import ooo.reindeer.logging.ILogger;
import ooo.reindeer.logging.LogUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class AutoGCQueueProxy implements IProxyFactory {

    private static final ILogger logger = LogUtil.getLogger(AutoGCQueueProxy.class);

    private static Map<String, Timestamp> cache = new ConcurrentHashMap<String, Timestamp>();

    static {
        Thread gcThread=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try{
                        int currentTime = SerialNumberUtil.getTimestamp();
                        for (Map.Entry<String, Timestamp> stringTimestampEntry : cache.entrySet()) {
                           if( currentTime - stringTimestampEntry.getValue().getValue() > 30){
                               cache.remove(stringTimestampEntry.getKey());
                               Cedf.unRegEventHandler(stringTimestampEntry.getKey());
                               logger.info("unRegEventHandler: type=[{}]",stringTimestampEntry.getKey());
                           }
                        }

                    }catch (Throwable e){
                    }finally {
                        try {
                            TimeUnit.SECONDS.sleep(30);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        });

        gcThread.setName("AutoGCQueueProxyThread");
        gcThread.setDaemon(true);
        gcThread.start();
    }

    @Override
    public <T> T proxy(T component, String id, Property config) {

        if (component instanceof IEventQueue && config.getValue("temp", false)) {
            logger.info("proxy: id=[{}], config=[{}]",id,config);
            Timestamp lastUpdateTime = new Timestamp(SerialNumberUtil.getTimestamp());
            cache.put(id, lastUpdateTime);
            return (T) new AutoGCQueue((IEventQueue) component, lastUpdateTime);
        } else {
            return component;
        }
    }

    private class AutoGCQueue implements IEventQueue {

        IEventQueue queue;
        Timestamp lastUpdateTime;

        AutoGCQueue(IEventQueue component, Timestamp timestamp) {
            queue = component;
            lastUpdateTime = timestamp;
        }

        @Override
        public boolean offer(Event event) {
            lastUpdateTime.setValue(SerialNumberUtil.getTimestamp());
            return queue.offer(event);
        }

        @Override
        public Event poll() {
            return queue.poll();
        }

        @Override
        public boolean regEventHandler(IEventHandler event_handler) {
            IEventHandler proxyhandler = new ProxyHandler(lastUpdateTime, event_handler);
            return queue.regEventHandler(proxyhandler);
        }

        @Override
        public boolean unRegEventHandler() {
            return queue.unRegEventHandler();
        }

        @Override
        public long size() {
            return queue.size();
        }

        @Override
        public boolean init(String id, Property config) {
            return queue.init(id, config);
        }

        @Override
        public boolean start() {
            return queue.start();
        }

        @Override
        public boolean stop() {
            return queue.stop();
        }

        @Override
        public String getId() {
            return queue.getId();
        }

        @Override
        public Property getConfig() {
            return queue.getConfig();
        }
    }

    private class ProxyHandler implements IEventHandler {

        IEventHandler handler;
        Timestamp lastUpdateTime;

        ProxyHandler(Timestamp lastUpdateTime, IEventHandler event_handler) {
            handler = event_handler;
            this.lastUpdateTime = lastUpdateTime;
        }

        @Override
        public Object onEvent(Event event) {
            lastUpdateTime.setValue(SerialNumberUtil.getTimestamp());
            return handler.onEvent(event);
        }

        @Override
        public boolean init(String id, Property config) {
            return handler.init(id, config);
        }


    }

    private class Timestamp {
        int value;

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }


        Timestamp(int timestamp) {
            value = timestamp;
        }


    }
}
